Skip to content

feat: push to Underlay with the v2 delta protocol - #3690

Merged
isTravis merged 1 commit into
mainfrom
worktree-underlay-delta-push
Oct 6, 2026
Merged

isTravis merged 1 commit into
mainfrom
worktree-underlay-delta-push

Conversation

@isTravis

@isTravis isTravis commented Oct 5, 2026

Copy link
Copy Markdown
Member

Underlay protocol v2 removes the negotiate protocol our "push to Underlay" uses. This moves the push to v2's delta push.

How it works now: the push reads the head version's manifest and diffs our records against it by (type, id) and hash. It then opens a push session, uploads only the upserts and deletes, and commits asynchronously.

Changes

  • Hashing: hash.ts hashes records and schemas the way the server does (RFC 8785 / JCS). The old sorted-object JSON.stringify ordered number-like keys differently. Records without such keys keep their existing hashes.
  • Push flow: client.ts reads the head's manifest in pages and diffs against it.
    • Uploads are batched within the server's line and byte limits.
    • The session is abandoned if the push fails partway.
    • A version conflict starts over once against the new head. "No changes detected" is treated as a no-op.
    • If a commit reports missing files, they're uploaded and the push starts a new session.
    • Files over 32 MB go through the presigned upload.
    • The readme is sent as a metadata patch, only when it differs, so metadata set in Underlay isn't overwritten.
  • Default base URL: now https://www.underlay.org/api. The apex domain's 301 turns POSTs into GETs.
  • Push cache: incremental.ts resolves cached records by (type, id) and corrects cache entries whose stored hashes are out of date.
  • Worker: the push cache is now also saved after a no-op, and the push log records the commit's added/updated/removed counts.
  • Settings fix: /accounts/me returns orgs, not accounts, so the org picker and the connection test's org check now work.

Testing: typecheck and biome pass, and the 107 Underlay unit tests pass. client.test.ts was rewritten against a fake Underlay push API. Read-only against staging, our record and schema hashes match the server's for 9,828 records. Not yet run: a real push against staging.

Deploy: don't merge before http://www.underlay.org switches to v2, because this client can't talk to v1. Ship it at the same time as that switch, and pause scheduled pushes in between.

Underlay v2 removes negotiate. A push now reads the head's manifest, diffs our
records against it by (type, id) and hash, and uploads only upserts and deletes
through a push session (POST /push, /records, /deletes, async /commit).

- hash: JCS (RFC 8785) written as a string, matching the server; the old
  sorted-object JSON.stringify put integer-like keys first. canonicalize stays
  for PubPub's own signatures.
- client: readHead (paged manifest, pinned to the head semver), diffAgainstHead,
  batches sized by the session's line and byte limits, session abandoned on
  failure, 409 conflict / "No changes detected" told apart, missing files
  uploaded then a new session, presigned upload for files over 32 MB, metadata
  sent as a patch of changed fields only, default base URL on www (the apex 301
  turns POSTs into GETs).
- incremental: resolve cache-hit records by (type, id) and self-heal cache
  entries whose stored hashes no longer match.
- worker: cache persisted on noop too; commit change counts in the push log.
- /accounts/me lists orgs as `orgs`, not `accounts`: the org picker and the
  connection test's org check now work.
@isTravis
isTravis merged commit ab8509d into main Oct 6, 2026
1 check passed
@isTravis
isTravis deleted the worktree-underlay-delta-push branch October 6, 2026 18:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant